4 追蹤者

資源

在 Yii 中,資源是指可以在網頁中引用的檔案。它可以是 CSS 檔案、JavaScript 檔案、圖片或影片檔案等。資源位於可透過網路存取的目錄中,並由 Web 伺服器直接提供服務。

以程式化的方式管理資源通常更為理想。例如,當您在頁面中使用 yii\jui\DatePicker 小工具時,它會自動包含所需的 CSS 和 JavaScript 檔案,而無需您手動尋找這些檔案並包含它們。當您將小工具升級到新版本時,它也會自動使用新版本的資源檔案。在本教學中,我們將描述 Yii 中提供的強大資源管理功能。

資源包

Yii 以資源包為單位管理資源。資源包只是一個位於目錄中的資源集合。當您在視圖中註冊資源包時,它會將資源包中的 CSS 和 JavaScript 檔案包含在呈現的網頁中。

定義資源包

資源包被指定為從 yii\web\AssetBundle 擴充的 PHP 類別。資源包的名稱僅僅是其對應的完整限定 PHP 類別名稱(不帶前導反斜線)。資源包類別應該是可自動載入的。它通常指定資源的位置、資源包包含哪些 CSS 和 JavaScript 檔案,以及資源包如何依賴其他資源包。

以下程式碼定義了 基本專案範本 使用的主要資源包

<?php

namespace app\assets;

use yii\web\AssetBundle;

class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.css',
        ['css/print.css', 'media' => 'print'],
    ];
    public $js = [
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap\BootstrapAsset',
    ];
}

上面的 AppAsset 類別指定資源檔案位於 @webroot 目錄下,該目錄對應於 URL @web;資源包包含單個 CSS 檔案 css/site.css,且不包含 JavaScript 檔案;資源包依賴於另外兩個資源包:yii\web\YiiAssetyii\bootstrap\BootstrapAssetyii\web\AssetBundle 屬性的更詳細說明可以在以下內容中找到

  • sourcePath:指定包含此資源包中資源檔案的根目錄。如果根目錄無法透過網路存取,則應設定此屬性。否則,您應該設定 basePath 屬性和 baseUrl 作為替代。路徑別名可以在這裡使用。
  • basePath:指定一個可透過網路存取的目錄,其中包含此資源包中的資源檔案。當您指定 sourcePath 屬性時,資源管理器將會將此資源包中的資源發佈到可透過網路存取的目錄,並相應地覆寫此屬性。如果您的資源檔案已經位於可透過網路存取的目錄中,並且不需要資源發佈,則應設定此屬性。路徑別名可以在這裡使用。
  • baseUrl:指定對應於目錄 basePath 的 URL。與 basePath 類似,如果您指定 sourcePath 屬性,資源管理器將會發佈資源並相應地覆寫此屬性。路徑別名可以在這裡使用。
  • css:一個陣列,列出此資源包中包含的 CSS 檔案。請注意,只有正斜線 "/" 應作為目錄分隔符號使用。每個檔案都可以單獨指定為字串,或者在陣列中與屬性標籤及其值一起指定。
  • js:一個陣列,列出此資源包中包含的 JavaScript 檔案。此陣列的格式與 css 的格式相同。每個 JavaScript 檔案都可以使用以下兩種格式之一指定
    • 一個相對路徑,表示一個本機 JavaScript 檔案(例如 js/main.js)。檔案的實際路徑可以通過將 yii\web\AssetManager::$basePath 前置到相對路徑來確定,而檔案的實際 URL 可以通過將 yii\web\AssetManager::$baseUrl 前置到相對路徑來確定。
    • 一個絕對 URL,表示一個外部 JavaScript 檔案。例如,https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
  • depends:一個陣列,列出此資源包所依賴的資源包名稱(稍後解釋)。
  • jsOptions:指定將傳遞給 yii\web\View::registerJsFile() 方法的選項,當呼叫該方法來註冊此資源包中的每個 JavaScript 檔案時。
  • cssOptions:指定將傳遞給 yii\web\View::registerCssFile() 方法的選項,當呼叫該方法來註冊此資源包中的每個 CSS 檔案時。
  • publishOptions:指定將傳遞給 yii\web\AssetManager::publish() 方法的選項,當呼叫該方法將來源資源檔案發佈到 Web 目錄時。這僅在您指定 sourcePath 屬性時使用。

資源位置

資源根據其位置,可以分為

  • 來源資源:資源檔案與 PHP 原始碼位於一起,無法透過 Web 直接存取。為了在頁面中使用來源資源,應將它們複製到 Web 目錄並轉換為所謂的發佈資源。此過程稱為資源發佈,將在稍後詳細描述。
  • 發佈資源:資源檔案位於 Web 目錄中,因此可以透過 Web 直接存取。
  • 外部資源:資源檔案位於與託管您的 Web 應用程式的伺服器不同的 Web 伺服器上。

在定義資源包類別時,如果您指定 sourcePath 屬性,則表示使用相對路徑列出的任何資源都將被視為來源資源。如果您未指定此屬性,則表示這些資源是發佈資源(因此您應該指定 basePathbaseUrl 以讓 Yii 知道它們的位置)。

建議您將屬於應用程式的資源放置在 Web 目錄中,以避免不必要的資源發佈過程。這就是為什麼先前範例中的 AppAsset 指定 basePath 而不是 sourcePath

對於擴充套件,由於它們的資源與其原始碼位於無法透過 Web 存取的目錄中,因此在為它們定義資源包類別時,您必須指定 sourcePath 屬性。

注意:請勿使用 @webroot/assets 作為 來源路徑。此目錄預設由 資源管理器 用於儲存從其來源位置發佈的資源檔案。此目錄中的任何內容都被視為暫時性的,可能會被移除。

資源相依性

當您在網頁中包含多個 CSS 或 JavaScript 檔案時,它們必須遵循一定的順序以避免覆寫問題。例如,如果您在網頁中使用 jQuery UI 小工具,則必須確保在 jQuery UI JavaScript 檔案之前包含 jQuery JavaScript 檔案。我們將這種順序稱為資源之間的相依性。

資源相依性主要透過 yii\web\AssetBundle::$depends 屬性指定。在 AppAsset 範例中,資源包依賴於另外兩個資源包:yii\web\YiiAssetyii\bootstrap\BootstrapAsset,這表示 AppAsset 中的 CSS 和 JavaScript 檔案將在兩個相依資源包中的檔案之後包含。

資源相依性是可傳遞的。這表示如果資源包 A 依賴於 B,而 B 依賴於 C,則 A 也將依賴於 C。

資源選項

您可以指定 cssOptionsjsOptions 屬性來自訂將 CSS 和 JavaScript 檔案包含在頁面中的方式。這些屬性的值將分別傳遞給 yii\web\View::registerCssFile()yii\web\View::registerJsFile() 方法,當 視圖 呼叫它們以包含 CSS 和 JavaScript 檔案時。

注意:您在資源包類別中設定的選項適用於資源包中的每個 CSS/JavaScript 檔案。如果您想要對不同的檔案使用不同的選項,則應使用上面提到的格式,或建立單獨的資源包,並在每個資源包中使用一組選項。

例如,若要有條件地為 IE9 或更低版本的瀏覽器包含 CSS 檔案,您可以使用以下選項

public $cssOptions = ['condition' => 'lte IE9'];

這將導致使用以下 HTML 標籤包含資源包中的 CSS 檔案

<!--[if lte IE9]>
<link rel="stylesheet" href="path/to/foo.css">
<![endif]-->

若要將產生的 CSS 連結標籤包裝在 <noscript> 中,您可以將 cssOptions 配置如下:

public $cssOptions = ['noscript' => true];

若要在頁面的 head 區段中包含 JavaScript 檔案(預設情況下,JavaScript 檔案包含在 body 區段的末尾),請使用以下選項

public $jsOptions = ['position' => \yii\web\View::POS_HEAD];

預設情況下,當發佈資源包時,將發佈由 yii\web\AssetBundle::$sourcePath 指定的目錄中的所有內容。您可以通過配置 publishOptions 屬性來自訂此行為。例如,若要僅發佈 yii\web\AssetBundle::$sourcePath 的一個或幾個子目錄,您可以在資源包類別中執行以下操作

<?php
namespace app\assets;

use yii\web\AssetBundle;

class FontAwesomeAsset extends AssetBundle 
{
    public $sourcePath = '@bower/font-awesome'; 
    public $css = [ 
        'css/font-awesome.min.css', 
    ];
    public $publishOptions = [
        'only' => [
            'fonts/*',
            'css/*',
        ]
    ];
}  

上面的範例定義了 "fontawesome" 套件的資源包。通過指定 only 發佈選項,將僅發佈 fontscss 子目錄。

Bower 和 NPM 資源安裝

大多數 JavaScript/CSS 套件由 Bower 和/或 NPM 套件管理器管理。在 PHP 世界中,我們有 Composer,它管理 PHP 相依性,但可以使用 composer.json 載入 Bower 和 NPM 套件,就像 PHP 套件一樣。

為了實現這一點,我們應該稍微配置一下我們的 composer。有兩種方法可以做到這一點


使用 asset-packagist 儲存庫

這種方式將滿足大多數需要 NPM 或 Bower 套件的專案的需求。

注意:自 2.0.13 起,基本和進階應用程式範本都預先配置為預設使用 asset-packagist,因此您可以跳過此區段。

在專案的 composer.json 中,新增以下幾行

"repositories": [
    {
        "type": "composer",
        "url": "https://asset-packagist.org"
    }
]

在您的應用程式配置中調整 @npm@bower 別名

$config = [
    ...
    'aliases' => [
        '@bower' => '@vendor/bower-asset',
        '@npm'   => '@vendor/npm-asset',
    ],
    ...
];

造訪 asset-packagist.org 以了解其運作方式。

使用 fxp/composer-asset-plugin

與 asset-packagist 相比,composer-asset-plugin 不需要對應用程式配置進行任何更改。相反,它需要通過執行以下命令全域安裝一個特殊的 Composer 外掛程式

composer global require "fxp/composer-asset-plugin:^1.4.1"

此命令全域安裝 composer asset plugin,允許通過 Composer 管理 Bower 和 NPM 套件相依性。安裝外掛程式後,您電腦上的每個專案都將通過 composer.json 支援 Bower 和 NPM 套件。

如果您想使用 Yii 發佈已安裝的套件,請將以下幾行新增到專案的 composer.json 中,以調整已安裝套件的放置目錄

"config": {
    "fxp-asset": {
        "installer-paths": {
            "npm-asset-library": "vendor/npm",
            "bower-asset-library": "vendor/bower"
        }
    }
}

注意:與 asset-packagist 相比,fxp/composer-asset-plugin 會顯著減慢 composer update 命令的速度。


在配置 Composer 以支援 Bower 和 NPM 之後

  1. 修改您的應用程式或擴充套件的 composer.json 檔案,並在 require 條目中列出套件。您應該使用 bower-asset/PackageName(對於 Bower 套件)或 npm-asset/PackageName(對於 NPM 套件)來引用程式庫。
  2. 執行 composer update
  3. 建立一個資源包類別,並列出您計劃在應用程式或擴充套件中使用的 JavaScript/CSS 檔案。您應該將 sourcePath 屬性指定為 @bower/PackageName@npm/PackageName。這是因為 Composer 會將 Bower 或 NPM 套件安裝在對應於此別名的目錄中。

注意:某些套件可能會將其所有發佈的檔案放在子目錄中。如果情況是這樣,您應該將子目錄指定為 sourcePath 的值。例如,yii\web\JqueryAsset 使用 @bower/jquery/dist 而不是 @bower/jquery

使用資源包

若要使用資源包,請通過呼叫 yii\web\AssetBundle::register() 方法在視圖中註冊它。例如,在視圖範本中,您可以像下面這樣註冊資源包

use app\assets\AppAsset;
AppAsset::register($this);  // $this represents the view object

資訊:yii\web\AssetBundle::register() 方法傳回一個資源包物件,其中包含有關已發佈資源的資訊,例如 basePathbaseUrl

如果您在其他位置註冊資源包,則應提供所需的視圖物件。例如,若要在小工具類別中註冊資源包,您可以通過 $this->view 取得視圖物件。

當資源包在視圖中註冊時,在幕後,Yii 將註冊其所有相依的資源包。如果資源包位於無法通過 Web 存取的目錄中,它將被發佈到 Web 目錄。稍後,當視圖呈現頁面時,它將為已註冊資源包中列出的 CSS 和 JavaScript 檔案產生 <link><script> 標籤。這些標籤的順序由已註冊資源包之間的相依性以及 yii\web\AssetBundle::$cssyii\web\AssetBundle::$js 屬性中列出的資源順序決定。

動態資源包

作為一個常規的 PHP 類別,資源包可以帶有一些與之相關的額外邏輯,並且可以動態調整其內部參數。例如:您可以使用一些複雜的 JavaScript 程式庫,它提供一些國際化功能,這些功能封裝在單獨的原始碼檔案中:每個檔案對應一種支援的語言。因此,您需要將特定的 '.js' 檔案新增到您的頁面,以使程式庫翻譯工作生效。這可以通過覆寫 yii\web\AssetBundle::init() 方法來實現

namespace app\assets;

use yii\web\AssetBundle;
use Yii;

class SophisticatedAssetBundle extends AssetBundle
{
    public $sourcePath = '/path/to/sophisticated/src';
    public $js = [
        'sophisticated.js' // file, which is always used
    ];

    public function init()
    {
        parent::init();
        $this->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added
    }
}

特定的資源包也可以通過 yii\web\AssetBundle::register() 傳回的實例進行調整。例如

use app\assets\SophisticatedAssetBundle;
use Yii;

$bundle = SophisticatedAssetBundle::register(Yii::$app->view);
$bundle->js[] = 'i18n/' . Yii::$app->language . '.js'; // dynamic file added

注意:儘管支援動態調整資源包,但這是一種不良實務,可能會導致意外的副作用,應盡可能避免。

自訂資源包

Yii 通過名為 assetManager 的應用程式元件管理資源包,該元件由 yii\web\AssetManager 實現。通過配置 yii\web\AssetManager::$bundles 屬性,可以自訂資源包的行為。例如,預設的 yii\web\JqueryAsset 資源包使用從已安裝的 jquery Bower 套件中的 jquery.js 檔案。為了提高可用性和效能,您可能想要使用 Google 託管的版本。這可以通過在應用程式配置中配置 assetManager 來實現,如下所示

return [
    // ...
    'components' => [
        'assetManager' => [
            'bundles' => [
                'yii\web\JqueryAsset' => [
                    'sourcePath' => null,   // do not publish the bundle
                    'js' => [
                        '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',
                    ]
                ],
            ],
        ],
    ],
];

您可以通過 yii\web\AssetManager::$bundles 類似地配置多個資源包。陣列鍵應該是資源包的類別名稱(不帶前導反斜線),陣列值應該是相應的配置陣列

提示:您可以有條件地選擇要在資源包中使用的資源。以下範例示範如何在開發環境中使用 jquery.js,否則使用 jquery.min.js

'yii\web\JqueryAsset' => [
    'js' => [
        YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'
    ]
],

您可以通過將 false 與您要停用的資源包名稱關聯來停用一個或多個資源包。當您在視圖中註冊已停用的資源包時,將不會註冊其任何相依的資源包,並且視圖也不會在其呈現的頁面中包含資源包中的任何資源。例如,若要停用 yii\web\JqueryAsset,您可以使用以下配置

return [
    // ...
    'components' => [
        'assetManager' => [
            'bundles' => [
                'yii\web\JqueryAsset' => false,
            ],
        ],
    ],
];

您還可以通過將 yii\web\AssetManager::$bundles 設定為 false 來停用所有資源包。

請記住,通過 yii\web\AssetManager::$bundles 進行的自訂應用於資源包的建立,例如在物件建構子階段。因此,在此之後對資源包物件進行的任何調整都將覆寫在 yii\web\AssetManager::$bundles 層級設定的映射。特別是:在 yii\web\AssetBundle::init() 方法內部或通過已註冊的資源包物件進行的調整將優先於 AssetManager 配置。以下是一些範例,其中通過 yii\web\AssetManager::$bundles 設定的映射不起作用

// Program source code:

namespace app\assets;

use yii\web\AssetBundle;
use Yii;

class LanguageAssetBundle extends AssetBundle
{
    // ...

    public function init()
    {
        parent::init();
        $this->baseUrl = '@web/i18n/' . Yii::$app->language; // can NOT be handled by `AssetManager`!
    }
}
// ...

$bundle = \app\assets\LargeFileAssetBundle::register(Yii::$app->view);
$bundle->baseUrl = YII_DEBUG ? '@web/large-files': '@web/large-files/minified'; // can NOT be handled by `AssetManager`!


// Application config :

return [
    // ...
    'components' => [
        'assetManager' => [
            'bundles' => [
                'app\assets\LanguageAssetBundle' => [
                    'baseUrl' => 'https://some.cdn.com/files/i18n/en' // makes NO effect!
                ],
                'app\assets\LargeFileAssetBundle' => [
                    'baseUrl' => 'https://some.cdn.com/files/large-files' // makes NO effect!
                ],
            ],
        ],
    ],
];

資源映射

有時您可能想要「修復」多個資源包中使用的不正確/不相容的資源檔案路徑。例如,資源包 A 使用 1.11.1 版本的 jquery.min.js,而資源包 B 使用 2.1.1 版本的 jquery.js。雖然您可以通過自訂每個資源包來解決此問題,但更簡單的方法是使用資源映射功能將不正確的資源映射到所需的資源。為此,請配置 yii\web\AssetManager::$assetMap 屬性,如下所示

return [
    // ...
    'components' => [
        'assetManager' => [
            'assetMap' => [
                'jquery.js' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js',
            ],
        ],
    ],
];

assetMap 的鍵是您想要修復的資源名稱,值是所需的資源路徑。當您在視圖中註冊資源包時,將針對此映射檢查其 cssjs 陣列中的每個相對資源檔案。如果發現任何鍵是資源檔案的最後一部分(如果可用,則以 yii\web\AssetBundle::$sourcePath 為前綴),則對應的值將取代該資源並在視圖中註冊。例如,資源檔案 my/path/to/jquery.js 符合鍵 jquery.js

注意:只有使用相對路徑指定的資源才會受到資源映射的影響。目標資源路徑應該是絕對 URL 或相對於 yii\web\AssetManager::$basePath 的路徑。

資源發佈

如前所述,如果資源包位於無法透過 Web 存取的目錄中,則當資源包在視圖中註冊時,其資源將被複製到 Web 目錄。此過程稱為資源發佈,由 資源管理器 自動完成。

預設情況下,資源發佈到目錄 @webroot/assets,該目錄對應於 URL @web/assets。您可以通過配置 basePathbaseUrl 屬性來自訂此位置。

如果您的作業系統和 Web 伺服器允許,您可以考慮使用符號連結而不是通過檔案複製來發佈資源。可以通過將 linkAssets 設定為 true 來啟用此功能。

return [
    // ...
    'components' => [
        'assetManager' => [
            'linkAssets' => true,
        ],
    ],
];

通過上述配置,資源管理器將在發佈資源包時建立到資源包來源路徑的符號連結。這比檔案複製更快,並且還可以確保發佈的資源始終是最新的。

快取失效

對於在生產模式下運行的 Web 應用程式,為資源和其他靜態資源啟用 HTTP 快取是一種常見的做法。這種做法的一個缺點是,每當您修改資源並將其部署到生產環境時,由於 HTTP 快取,用戶端可能仍在使用舊版本。為了克服這個缺點,您可以使用版本 2.0.3 中引入的快取失效功能,方法是配置 yii\web\AssetManager,如下所示

return [
    // ...
    'components' => [
        'assetManager' => [
            'appendTimestamp' => true,
        ],
    ],
];

通過這樣做,每個發佈資源的 URL 都將附加其最後修改時間戳記。例如,yii.js 的 URL 可能看起來像 /assets/5515a87c/yii.js?v=1423448645",其中參數 v 表示 yii.js 檔案的最後修改時間戳記。現在,如果您修改資源,其 URL 也會更改,這會導致用戶端提取最新版本的資源。

常用資源包

Yii 核心程式碼定義了許多資源包。其中,以下資源包是常用的,可以在您的應用程式或擴充套件程式碼中引用。

  • yii\web\YiiAsset:它主要包含 yii.js 檔案,該檔案實現了一種在模組中組織 JavaScript 程式碼的機制。它還為 data-methoddata-confirm 屬性以及其他有用的功能提供特殊支援。有關 yii.js 的更多資訊,請參閱用戶端腳本區段
  • yii\web\JqueryAsset:它包含了來自 jQuery Bower 套件的 jquery.js 檔案。
  • yii\bootstrap\BootstrapAsset:它包含了來自 Twitter Bootstrap 框架的 CSS 檔案。
  • yii\bootstrap\BootstrapPluginAsset:它包含了來自 Twitter Bootstrap 框架的 JavaScript 檔案,用於支援 Bootstrap JavaScript 外掛程式。
  • yii\jui\JuiAsset:它包含了來自 jQuery UI 函式庫的 CSS 和 JavaScript 檔案。

如果您的程式碼依賴 jQuery、jQuery UI 或 Bootstrap,您應該使用這些預定義的資源包,而不是建立您自己的版本。如果這些資源包的預設設定不符合您的需求,您可以按照自訂資源包小節中的描述進行自訂。

資源轉換

開發人員通常會使用一些擴展語法編寫 CSS 和/或 JavaScript 程式碼,並使用特殊工具將其轉換為 CSS/JavaScript,而不是直接編寫 CSS 和/或 JavaScript 程式碼。例如,對於 CSS 程式碼,您可以使用 LESSSCSS;而對於 JavaScript,您可以使用 TypeScript

您可以在資源包的 cssjs 屬性中列出使用擴展語法的資源檔案。例如:

class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.less',
    ];
    public $js = [
        'js/site.ts',
    ];
    public $depends = [
        'yii\web\YiiAsset',
        'yii\bootstrap\BootstrapAsset',
    ];
}

當您向視圖註冊這樣的資源包時,資源管理器將自動運行預處理器工具,將已識別的擴展語法資源轉換為 CSS/JavaScript。當視圖最終渲染頁面時,它將在頁面中包含 CSS/JavaScript 檔案,而不是原始的擴展語法資源。

Yii 使用檔案名稱副檔名來識別資源所使用的擴展語法。預設情況下,它識別以下語法和檔案名稱副檔名:

Yii 依賴已安裝的預處理器工具來轉換資源。例如,要使用 LESS,您應該安裝 lessc 預處理器命令。

您可以通過配置 yii\web\AssetManager::$converter 來客製化預處理器命令和支援的擴展語法,如下所示:

return [
    'components' => [
        'assetManager' => [
            'converter' => [
                'class' => 'yii\web\AssetConverter',
                'commands' => [
                    'less' => ['css', 'lessc {from} {to} --no-color'],
                    'ts' => ['js', 'tsc --out {to} {from}'],
                ],
            ],
        ],
    ],
];

在上面,我們通過 yii\web\AssetConverter::$commands 屬性指定了支援的擴展語法。陣列鍵是檔案副檔名(不帶前導點),陣列值是結果資源檔案副檔名和用於執行資源轉換的命令。命令中的符號 {from}{to} 將被替換為來源資源檔案路徑和目標資源檔案路徑。

資訊:除了上面描述的方法外,還有其他方法可以使用擴展語法資源。例如,您可以使用構建工具,如 grunt 來監控和自動轉換擴展語法資源。在這種情況下,您應該在資源包中列出生成的 CSS/JavaScript 檔案,而不是原始檔案。

組合與壓縮資源

一個網頁可以包含許多 CSS 和/或 JavaScript 檔案。為了減少 HTTP 請求的數量和這些檔案的整體下載大小,常見的做法是將多個 CSS/JavaScript 檔案組合和壓縮成一個或非常少的檔案,然後在網頁中包含這些壓縮後的檔案,而不是原始檔案。

資訊:當應用程式處於生產模式時,通常需要組合和壓縮資源。在開發模式下,使用原始的 CSS/JavaScript 檔案通常更方便於除錯。

在下面,我們介紹一種無需修改現有應用程式程式碼即可組合和壓縮資源檔案的方法。

  1. 找出您應用程式中計劃組合和壓縮的所有資源包。
  2. 將這些資源包分成一個或幾個群組。請注意,每個資源包只能屬於一個群組。
  3. 將每個群組中的 CSS 檔案組合/壓縮成單個檔案。對 JavaScript 檔案也執行類似的操作。
  4. 為每個群組定義一個新的資源包。
    • cssjs 屬性設定為組合後的 CSS 和 JavaScript 檔案。
    • 通過將每個群組中資源包的 cssjs 屬性設定為空,並將其 depends 屬性設定為為該群組建立的新資源包,來自訂這些資源包。

使用這種方法,當您在視圖中註冊一個資源包時,它會導致自動註冊該原始資源包所屬群組的新資源包。因此,組合/壓縮後的資源檔案將包含在頁面中,而不是原始檔案。

範例

讓我們用一個範例進一步解釋上述方法。

假設您的應用程式有兩個頁面,X 和 Y。頁面 X 使用資源包 A、B 和 C,而頁面 Y 使用資源包 B、C 和 D。

您有兩種方法可以劃分這些資源包。一種是使用單一群組來包含所有資源包,另一種是將 A 放入群組 X,D 放入群組 Y,以及將 (B, C) 放入群組 S。哪種方法更好?這取決於情況。第一種方法的優點是兩個頁面共享相同的組合 CSS 和 JavaScript 檔案,這使得 HTTP 快取更有效。另一方面,由於單一群組包含所有資源包,因此組合 CSS 和 JavaScript 檔案的大小將更大,從而增加初始檔案傳輸時間。為了簡化此範例,我們將使用第一種方法,即使用單一群組來包含所有資源包。

資訊:將資源包劃分到群組中並非易事。這通常需要分析不同頁面上各種資源的真實世界流量數據。在開始時,您可以從單一群組開始以簡化操作。

使用現有工具(例如 Closure CompilerYUI Compressor)組合和壓縮所有資源包中的 CSS 和 JavaScript 檔案。請注意,檔案應按照滿足資源包之間依賴關係的順序進行組合。例如,如果資源包 A 依賴於 B,而 B 又依賴於 C 和 D,那麼您應該從 C 和 D 開始列出資源檔案,然後是 B,最後是 A。

在組合和壓縮後,我們得到一個 CSS 檔案和一個 JavaScript 檔案。假設它們被命名為 all-xyz.cssall-xyz.js,其中 xyz 代表時間戳記或雜湊值,用於使檔案名稱唯一,以避免 HTTP 快取問題。

我們現在來到最後一步。在應用程式設定中配置資源管理器,如下所示:

return [
    'components' => [
        'assetManager' => [
            'bundles' => [
                'all' => [
                    'class' => 'yii\web\AssetBundle',
                    'basePath' => '@webroot/assets',
                    'baseUrl' => '@web/assets',
                    'css' => ['all-xyz.css'],
                    'js' => ['all-xyz.js'],
                ],
                'A' => ['css' => [], 'js' => [], 'depends' => ['all']],
                'B' => ['css' => [], 'js' => [], 'depends' => ['all']],
                'C' => ['css' => [], 'js' => [], 'depends' => ['all']],
                'D' => ['css' => [], 'js' => [], 'depends' => ['all']],
            ],
        ],
    ],
];

自訂資源包小節中所述,以上設定更改了每個資源包的預設行為。特別是,資源包 A、B、C 和 D 不再有任何資源檔案。它們現在都依賴於包含組合後的 all-xyz.cssall-xyz.js 檔案的 all 資源包。因此,對於頁面 X,將只包含這兩個組合檔案,而不是包含來自資源包 A、B 和 C 的原始來源檔案;頁面 Y 也發生同樣的事情。

還有一個最終技巧可以使上述方法更順利地工作。您可以將資源包自訂陣列放在一個單獨的檔案中,並有條件地將此檔案包含在應用程式設定中,而不是直接修改應用程式設定檔案。例如:

return [
    'components' => [
        'assetManager' => [
            'bundles' => require __DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php'),  
        ],
    ],
];

也就是說,資源包設定陣列保存在生產模式下的 assets-prod.php 和非生產模式下的 assets-dev.php 中。

注意:此資源組合機制基於 yii\web\AssetManager::$bundles 覆蓋已註冊資源包屬性的能力。但是,正如上面已經說過的,這種能力不涵蓋在 yii\web\AssetBundle::init() 方法或資源包註冊後執行的資源包調整。您應該避免在資源組合期間使用此類動態資源包。

使用 asset 命令

Yii 提供了一個名為 asset 的控制台命令,以自動化我們剛才描述的方法。

要使用此命令,您應首先建立一個設定檔,以描述應組合哪些資源包以及應如何對它們進行分組。您可以先使用 asset/template 子命令生成一個模板,然後修改它以適合您的需求。

yii asset/template assets.php

該命令在目前目錄中生成一個名為 assets.php 的檔案。此檔案的內容如下所示:

<?php
/**
 * Configuration file for the "yii asset" console command.
 * Note that in the console environment, some path aliases like '@webroot' and '@web' may not exist.
 * Please define these missing path aliases.
 */
return [
    // Adjust command/callback for JavaScript files compressing:
    'jsCompressor' => 'java -jar compiler.jar --js {from} --js_output_file {to}',
    // Adjust command/callback for CSS files compressing:
    'cssCompressor' => 'java -jar yuicompressor.jar --type css {from} -o {to}',
    // Whether to delete asset source after compression:
    'deleteSource' => false,
    // The list of asset bundles to compress:
    'bundles' => [
        // 'yii\web\YiiAsset',
        // 'yii\web\JqueryAsset',
    ],
    // Asset bundle for compression output:
    'targets' => [
        'all' => [
            'class' => 'yii\web\AssetBundle',
            'basePath' => '@webroot/assets',
            'baseUrl' => '@web/assets',
            'js' => 'js/all-{hash}.js',
            'css' => 'css/all-{hash}.css',
        ],
    ],
    // Asset manager configuration:
    'assetManager' => [
    ],
];

您應該修改此檔案,並在 bundles 選項中指定您計劃組合的資源包。在 targets 選項中,您應該指定應如何將資源包劃分到群組中。您可以指定一個或多個群組,如前所述。

注意:由於別名 @webroot@web 在控制台應用程式中不可用,因此您應在設定中明確定義它們。

JavaScript 檔案被組合、壓縮並寫入 js/all-{hash}.js,其中 {hash} 被替換為結果檔案的雜湊值。

jsCompressorcssCompressor 選項指定用於執行 JavaScript 和 CSS 組合/壓縮的控制台命令或 PHP 回調。預設情況下,Yii 使用 Closure Compiler 用於組合 JavaScript 檔案,並使用 YUI Compressor 用於組合 CSS 檔案。您應該手動安裝這些工具或調整這些選項以使用您喜歡的工具。

有了設定檔,您可以運行 asset 命令來組合和壓縮資源檔案,然後生成一個新的資源包設定檔 assets-prod.php

yii asset assets.php config/assets-prod.php

生成的設定檔可以包含在應用程式設定中,如最後一個小節所述。

注意:如果您通過 yii\web\AssetManager::$bundlesyii\web\AssetManager::$assetMap 為您的應用程式自訂資源包,並希望將此自訂應用於壓縮來源檔案,則應將這些選項包含到資源命令設定檔中的 assetManager 區段中。

注意:在指定壓縮來源時,您應避免使用其參數可能會動態調整(例如在 init() 方法或註冊後)的資源包,因為它們在壓縮後可能會無法正常工作。

資訊:使用 asset 命令不是自動化資源組合和壓縮過程的唯一選擇。您可以使用出色的任務執行器工具 grunt 來達到相同的目標。

資源包分組

在最後一個小節中,我們解釋了如何將所有資源包組合到單個資源包中,以最大程度地減少應用程式中引用的資源檔案的 HTTP 請求。但在實務中,這並不總是理想的。例如,假設您的應用程式有一個「前端」和一個「後端」,它們各自使用不同的 JavaScript 和 CSS 檔案集。在這種情況下,將前端和後端的所有資源包組合到單個資源包中是沒有意義的,因為「後端」不使用「前端」的資源包,並且在請求「前端」頁面時發送「後端」資源將會浪費網路頻寬。

要解決上述問題,您可以將資源包劃分到群組中,並組合每個群組的資源包。以下設定顯示了如何對資源包進行分組:

return [
    ...
    // Specify output bundles with groups:
    'targets' => [
        'allShared' => [
            'js' => 'js/all-shared-{hash}.js',
            'css' => 'css/all-shared-{hash}.css',
            'depends' => [
                // Include all assets shared between 'backend' and 'frontend'
                'yii\web\YiiAsset',
                'app\assets\SharedAsset',
            ],
        ],
        'allBackEnd' => [
            'js' => 'js/all-{hash}.js',
            'css' => 'css/all-{hash}.css',
            'depends' => [
                // Include only 'backend' assets:
                'app\assets\AdminAsset'
            ],
        ],
        'allFrontEnd' => [
            'js' => 'js/all-{hash}.js',
            'css' => 'css/all-{hash}.css',
            'depends' => [], // Include all remaining assets
        ],
    ],
    ...
];

如您所見,資源包被劃分為三個群組:allSharedallBackEndallFrontEnd。它們各自依賴於一組適當的資源包。例如,allBackEnd 依賴於 app\assets\AdminAsset。當使用此設定運行 asset 命令時,它將根據上述規範組合資源包。

資訊:您可以將其中一個目標資源包的 depends 設定留空。這樣做,該特定資源包將依賴於其他目標資源包不依賴的所有剩餘資源包。

發現錯字或您認為此頁面需要改進?
在 github 上編輯 !